import json
import os
import tkinter as tk
from tkinter import ttk, scrolledtext
import component as c
import threading
import queue

from code_scanner import count_code, format_stats


class CodeLineCounterGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("代码行数统计工具-v2.2")
        width_ = screen_width // 5 * 4
        height_ = screen_height // 5 * 4
        self.root.geometry(f"{width_}x{height_}")  # 窗口大小
        # self.root.geometry("1200x900")  # 窗口大小

        # 创建队列用于线程间通信
        self.task_queue = queue.Queue()
        # 定期检查队列（主线程中执行）
        self.check_queue()

        # 配置文件路径（跨平台）
        self.config_dir = os.path.join(
            os.path.expanduser("~"),
            ".config" if os.name != "nt" else os.environ.get("APPDATA", "~"),
            "CodeLineCounter"
        )
        self.config_path = os.path.join(self.config_dir, "config.json")

        # 创建配置目录（如果不存在）
        os.makedirs(self.config_dir, exist_ok=True)

        self.repo_path_entry = scrolledtext.ScrolledText(self.root, height=10, width=80)
        # 加载保存的作者
        self.authors_entry = c.TipEntry(self.root, self.load_config().get("last_authors", ""),
                                        "多个开发者，用空格分开...")
        self.authors_entry.bind('<FocusOut>', self.on_focus_out_author)  # 绑定焦点离开事件
        # 加载保存的路径
        self.saved_path = self.load_config().get("last_repo_path", "")
        self.repo_path_entry.insert(tk.END, self.saved_path)  # 自动填充输入框
        # 起止时间
        self.start_date_entry = c.TipEntry(self.root, self.load_config().get("start_date", ""),
                                           '时间格式：yyyy-mm-dd，包含开始时间', width=50)
        self.start_date_entry.bind('<FocusOut>', self.on_focus_out_start_date)  # 绑定焦点离开事件
        self.end_date_entry = c.TipEntry(self.root, self.load_config().get("end_date", ""),
                                         '时间格式：yyyy-mm-dd，不包含结束时间', width=50)
        self.end_date_entry.bind('<FocusOut>', self.on_focus_out_end_date)  # 绑定焦点离开事件

        self.branch_entry = c.TipEntry(self.root, self.load_config().get("branch", ""),
                                       "填写单个分支（不允许同时填写多个分支），当然也可以空着（空着相当于扫描所有分支）")
        self.branch_entry.bind('<FocusOut>', self.on_focus_out_branch)  # 绑定焦点离开事件

        self.diff_result_entry = c.TipEntry(self.root, self.load_config().get("diff_result", ""),
                                            "填写临时保存 diff 结果的 目录（注意是 目录！！！未填写就不保存 diff 结果）")
        self.diff_result_entry.bind('<FocusOut>', self.on_focus_out_diff)  # 绑定焦点离开事件

        tk.Label(self.root, text="fetch代码：").grid(row=6, column=0, padx=10, sticky="e")
        self.is_pull = tk.StringVar()
        tk.Radiobutton(self.root, text='是', variable=self.is_pull, value='1').grid(row=6, column=1, padx=0, pady=0, sticky="w")
        tk.Radiobutton(self.root, text='否', variable=self.is_pull, value='0').grid(row=6, column=2, padx=0, pady=0, sticky="e")
        self.is_pull.set('0')

        self.static_button_entry = tk.Button(self.root, text="开始统计", command=self.start_async_task, bg="#4CAF50",
                                             fg="black",
                                             font=("微软雅黑", 10))

        self.result_text = scrolledtext.ScrolledText(self.root, height=15, width=2000, wrap='none')
        # 创建横向滚动条（orient='horizontal'）
        x_scrollbar = ttk.Scrollbar(root, orient="horizontal", command=self.result_text.xview)
        x_scrollbar.grid(row=9, column=1, columnspan=10, sticky="enw")
        # 绑定滚动条与 Text 组件（双向绑定）
        self.result_text.config(xscrollcommand=x_scrollbar.set)  # Text 滚动时更新滚动条
        x_scrollbar.config(command=self.result_text.xview)  # 滚动条拖动时更新 Text 视图
        # 创建界面组件
        self.create_widgets()

    def start_async_task(self):
        """启动异步任务（子线程）"""
        # 禁用按钮防止重复点击
        self.static_button_entry.config(state="disabled")
        # self.status_label.config(text="状态：任务执行中...")

        # 创建并启动子线程
        thread = threading.Thread(target=self.long_running_task)
        thread.daemon = True  # 子线程随主线程退出
        thread.start()

    def long_running_task(self):
        """模拟耗时任务（子线程中执行）"""
        try:
            # 模拟耗时操作（如文件读取、网络请求）
            self.run_count()
            # 任务完成，放入完成信息
            self.task_queue.put(("complete", "任务完成！"))

        except Exception as e:
            # 异常信息放入队列
            self.task_queue.put(("error", f"任务失败：{str(e)}"))

    def check_queue(self):
        """主线程定期检查队列，更新界面"""
        try:
            while True:
                # 从队列中获取任务结果（非阻塞）
                msg_type, msg = self.task_queue.get_nowait()

                if msg_type == "progress":
                    # self.status_label.config(text=f"状态：{msg}")
                    pass
                elif msg_type == "complete":
                    # self.status_label.config(text=f"状态：{msg}")
                    self.static_button_entry.config(state="normal")  # 恢复按钮
                elif msg_type == "error":
                    # self.status_label.config(text=f"状态：{msg}")
                    self.static_button_entry.config(state="normal")  # 恢复按钮

                # 清空队列（避免重复处理）
                self.task_queue.task_done()

        except queue.Empty:
            # 队列为空，继续定期检查
            pass

        # 每隔100ms检查一次队列（保持界面响应）
        self.root.after(100, self.check_queue)

    def create_widgets(self):
        # 1.仓库路径输入
        tk.Label(self.root, text="仓库路径：").grid(row=0, column=0, padx=10, pady=5, sticky="e")
        self.repo_path_entry.grid(row=0, column=1, columnspan=9, padx=0, pady=5, sticky="ew")
        tk.Button(self.root, text="浏览...", command=self.browse_folder_repo_path).grid(row=0, column=10, padx=10)
        # 2.作者
        tk.Label(self.root, text="开发者：").grid(row=1, column=0, padx=10, pady=5, sticky="e")
        self.authors_entry.grid(row=1, column=1, columnspan=9, padx=0, pady=5, sticky="ew")
        # 3.起止时间
        tk.Label(self.root, text="开始时间：").grid(row=2, column=0, padx=10, pady=5, sticky="e")
        self.start_date_entry.grid(row=2, column=1, padx=0, pady=5, sticky="w")
        tk.Label(self.root, text="结束时间：").grid(row=3, column=0, padx=10, pady=5, sticky="e")
        self.end_date_entry.grid(row=3, column=1, padx=0, pady=5, sticky="w")
        tk.Label(self.root, text="指定分支：").grid(row=4, column=0, padx=10, pady=5, sticky="e")
        self.branch_entry.grid(row=4, column=1, columnspan=9, padx=0, pady=5, sticky="ew")
        # 4.diff 结果
        tk.Label(self.root, text="diff结果：").grid(row=5, column=0, padx=10, pady=5, sticky="e")
        self.diff_result_entry.grid(row=5, column=1, columnspan=9, padx=0, pady=5, sticky="ew")
        tk.Button(self.root, text="浏览...", command=self.browse_folder_diff_result).grid(row=5, column=10, padx=10)
        # 5.执行按钮
        self.static_button_entry.grid(row=7, column=4, padx=0, pady=0, sticky="w")
        # 6.结果显示区域
        tk.Label(self.root, text="统计结果：").grid(row=8, column=0, padx=10, pady=5, sticky="new")
        self.result_text.grid(row=8, column=1, columnspan=10, sticky="nsew")

        # 调整列权重（使输入框随窗口拉伸）
        # self.root.columnconfigure(1, weight=1)
        # self.root.columnconfigure(2, weight=1)
        self.root.columnconfigure(3, weight=1)
        self.root.columnconfigure(4, weight=1)
        self.root.columnconfigure(5, weight=1)
        self.root.columnconfigure(6, weight=1)
        self.root.columnconfigure(7, weight=1)
        self.root.columnconfigure(8, weight=1)
        self.root.columnconfigure(9, weight=1)
        self.root.rowconfigure(8, weight=1)

    def browse_folder_repo_path(self):
        """浏览文件夹并填充路径"""
        from tkinter import filedialog
        init_dir = self.saved_path.split()[-1] if self.saved_path and self.saved_path.split()[-1] else ""
        folder_path = filedialog.askdirectory(
            title="选择目录",
            initialdir=init_dir
        )
        if folder_path:
            self.repo_path_entry.insert(tk.END,
                                        ('\n' if self.repo_path_entry.get('0.0', tk.END).strip() else '') + folder_path)
            self.save_config()

    def browse_folder_diff_result(self):
        """浏览文件夹并填充路径"""
        from tkinter import filedialog
        init_dir = self.load_config().get("branch", "")
        folder_path = filedialog.askdirectory(
            title="选择目录",
            initialdir=init_dir
        )
        if folder_path:
            self.diff_result_entry.delete(0, tk.END)
            self.diff_result_entry.insert(tk.END, folder_path)
            self.save_config()

    def on_focus_out_author(self, event):
        self.authors_entry.on_focus_out(event)
        self.save_config()

    def on_focus_out_start_date(self, event):
        self.start_date_entry.on_focus_out(event)
        self.save_config()

    def on_focus_out_end_date(self, event):
        self.end_date_entry.on_focus_out(event)
        self.save_config()

    def on_focus_out_branch(self, event):
        self.branch_entry.on_focus_out(event)
        self.save_config()

    def on_focus_out_diff(self, event):
        self.diff_result_entry.on_focus_out(event)
        self.save_config()

    def save_config(self, event=None):
        config = self.load_config()  # 读取现有配置
        # 仓库路径
        path = self.repo_path_entry.get('0.0', tk.END).strip()
        if path:  # 仅保存非空路径
            config["last_repo_path"] = path
            self.saved_path = path  # 更新内存中的保存路径
        # 作者
        authors = self.authors_entry.get().strip()
        if authors:
            config["last_authors"] = authors
        # 开始时间
        if self.start_date_entry.get():
            config['start_date'] = self.start_date_entry.get()
        # 结束时间
        if self.end_date_entry.get():
            config['end_date'] = self.end_date_entry.get()
        config['branch'] = self.branch_entry.get()
        config['diff_result'] = self.diff_result_entry.get()
        # 写回配置文件到磁盘
        with open(self.config_path, "w", encoding="utf-8") as f:
            json.dump(config, f, ensure_ascii=False, indent=2)

    def load_config(self):
        """加载配置文件（不存在时返回空字典）"""
        try:
            with open(self.config_path, "r", encoding="utf-8") as f:
                return json.load(f)
        except (FileNotFoundError, json.JSONDecodeError):
            return {}  # 配置文件不存在或损坏时返回空

    def run_count(self):
        # 定义表格列宽（根据内容长度调整，确保对齐）
        col_width = 15  # 每列固定宽度（可根据实际内容增减）
        col_repo = col_width * 4
        col_num = col_width - 1
        self.save_config()
        """执行统计并显示结果"""
        repo_path = self.repo_path_entry.get('0.0', tk.END).strip()
        self.result_text.delete(0.0, tk.END)  # 清空旧结果

        # 输入校验
        if not repo_path:
            self.result_text.insert(tk.END, "错误：仓库路径不能为空！\n")
            return
        repos = repo_path.split()
        for repo in repos:
            if not os.path.isdir(repo):
                self.result_text.insert(tk.END, f"错误：仓库路径[{repo}]不存在，请检查！\n")
                return
            if ".git" not in os.listdir(repo):
                self.result_text.insert(tk.END, f"警告：'{repo}' 不是有效的Git仓库\n")
                return

        branch = self.branch_entry.get()
        diff_result = self.diff_result_entry.get()

        # 调用原有统计函数
        try:
            self.result_text.insert(tk.END, "小二正在给您上菜，请稍等...\n")
            result = count_code(repos, self.authors_entry.get().split(), self.start_date_entry.get(),
                                self.end_date_entry.get(), None if branch == self.branch_entry.tip_text else branch,
                                None if diff_result == self.diff_result_entry.tip_text else diff_result, self.is_pull.get() == '1')
            self.result_text.delete(0.0, tk.END)  # 清空旧结果
            if not result:
                self.result_text.insert(tk.END, "空空如也...\n")
            # 显示结果
            for author, all_stats in result:
                # 输出汇总结果
                if not all_stats:
                    continue
                gather_total = []
                sum_total = 0
                for repo, stats in all_stats:
                    output, totals = format_stats(repo, stats, author)
                    gather_total.append((repo, totals, output))
                for _, totals, _ in gather_total:
                    sum_total += sum(totals["added"].values()) + sum(totals["removed"].values())
                print('-' * 300)
                print(f"提交人：{author} - 总行数：{sum_total}\n")
                self.result_text.insert(tk.END, '-' * 300 + '\n')
                self.result_text.insert(tk.END, f"提交人：{author} - 总行数：{sum_total}\n\n")
                for repo, totals, _ in gather_total:
                    s = (f"| 仓库：{repo:<{col_repo}}|"  # 仓库名列宽加倍（内容较长）
                         f"总变更行数：{sum(totals['added'].values()) + sum(totals['removed'].values()):<{col_num}}|"
                         f"新增代码行：{totals['added']['code']:<{col_num}}|"
                         f"新增注释行：{totals['added']['comment']:<{col_num}}|"
                         f"新增空行：{totals['added']['blank']:<{col_num}}|"
                         f"删除代码行：{totals['removed']['code']:<{col_num}}|"
                         f"删除注释行：{totals['removed']['comment']:<{col_num}}|"
                         f"删除空行：{totals['removed']['blank']:<{col_num}}|")
                    # 打印表格标题（左对齐）
                    print(s)
                    self.result_text.insert(tk.END, s + '\n')
                for *_, output in gather_total:
                    print(output)
                    self.result_text.insert(tk.END, output + '\n')
                self.result_text.insert(tk.END, '\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n')
        except Exception as e:
            self.result_text.insert(tk.END, f"执行失败：{str(e)}\n")


def location_window(root):
    # 设置窗口尺寸（若未指定则自动适应内容）
    root.update_idletasks()  # 强制更新窗口尺寸
    width = root.winfo_width()
    height = root.winfo_height()

    x = (screen_width - width) // 8
    y = (screen_height - height) // 8

    # 定位窗口到居中位置
    root.geometry(f"+{x}+{y}")


def set_focus(event):
    root.focus_set()  # 窗口显示后设置焦点


if __name__ == "__main__":
    root = tk.Tk()
    # 获取屏幕尺寸（单位：像素）
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    location_window(root)
    app = CodeLineCounterGUI(root)
    # 窗口置顶（部分系统需配合 focus_set）
    # root.attributes('-topmost', True)
    root.focus_set()  # 窗口显示后设置焦点
    # 绑定窗口显示事件（窗口首次显示时触发）
    root.bind("<Map>", set_focus)
    root.mainloop()
